﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Threading;
using Jmr.Serialization;
using System.IO;
using System.Collections.Generic;
using Jmr.Compression;
using System.Collections;

namespace Jmr.Entity.Poco.Silverlight
{

	public delegate void FetchAllDocumentsForUserCompletedEventHandler(FetchResultEventArgs e);
	public delegate void FetchAllDocumentsForUserByBuildingCompletedEventHandler(FetchResultEventArgs e);
	/// <summary>
	/// IDocumentumEntitySessionProxy interface.
	/// </summary>
	public interface IDocumentumEntitySessionProxy
	{
		/// <summary>
		/// IDocumentumEntitySessionProxy.FetchAllDocumentsForUserCompletedEventHandler event.
		/// </summary>
		event FetchAllDocumentsForUserCompletedEventHandler OnFetchAllDocumentsForUserCompleted;

		/// <summary>
		/// IDocumentumEntitySessionProxy.FetchAllDocumentsForUserByBuildingCompletedEventHandler event.
		/// </summary>
		event FetchAllDocumentsForUserByBuildingCompletedEventHandler OnFetchAllDocumentsForUserByBuildingCompleted;

		void FetchAllByProperty<TEntity>(string code, string userName);
		void FetchAllByBuilding<TEntity>(string code, string userName);
	}

	public class DocumentumEntitySessionFactory
	{
		public static IDocumentumEntitySessionProxy CreateSession(string uri)
		{
			return new DocumentumEntitySessionProxy(uri);
		}
	}

	public class DocumentumEntitySessionProxy : IDocumentumEntitySessionProxy
	{
		private SynchronizationContext _syncContext;
		private string _uristring = null;
		private Uri _uri = null;
		private EntityFormatter _formatter = null;
		private BinaryEntityFormatter _binaryformatter = null;

		public DocumentumEntitySessionProxy(string uri)
		{
			_uristring = uri.ToLower();
			_uri = new Uri(_uristring);
			_binaryformatter = new BinaryEntityFormatter();
			_syncContext = SynchronizationContext.Current;
		}

		private WebRequest GetWebRequest(DocumentumAction action, string entityassemblyname, string entityfullname, string userName, string code)
		{
			var webrequest = HttpWebRequest.Create(_uri);
			webrequest.Headers["Culture"] = System.Globalization.CultureInfo.CurrentCulture.ToString();
			webrequest.Headers["action"] = action.ToString();
			webrequest.Headers["entityfullname"] = entityfullname;
			webrequest.Headers["entityassemblyname"] = entityassemblyname;
			webrequest.Headers["userName"] = userName;
			webrequest.Headers["code"] = code;
			webrequest.Method = "POST";
			return webrequest;
		}

		#region IDocumentumEntitySessionProxy Members

		public event FetchAllDocumentsForUserCompletedEventHandler OnFetchAllDocumentsForUserCompleted;

		public event FetchAllDocumentsForUserByBuildingCompletedEventHandler OnFetchAllDocumentsForUserByBuildingCompleted;

		#endregion

    public static event ExceptionEventHandler OnDocumentumException;

		public void FetchAllByProperty<TEntity>(string code, string userName)
		{
			DoAction<TEntity>(DocumentumAction.FetchAllByProperty, userName, code);
		}

		public void FetchAllByBuilding<TEntity>(string code, string userName)
		{
			DoAction<TEntity>(DocumentumAction.FetchAllByBuilding, userName, code);
		}

		public void DoAction<TEntity>(DocumentumAction action, string userName, string code)
		{
			var documentumWebRequest = GetWebRequest(action, typeof(TEntity).Assembly.FullName, typeof(TEntity).FullName, userName, code);
			var entityarg = new byte[0];
			documentumWebRequest.BeginGetRequestStream(new AsyncCallback(ReadCallback), new EntityArgRequest { EntityArg = entityarg, Request = documentumWebRequest });
		}

		private void ReadCallback(IAsyncResult asynchronousResult)
		{
			var argRequest = (EntityArgRequest)asynchronousResult.AsyncState;
			var entityarg = argRequest.EntityArg;
			var request = argRequest.Request;
			var postStream = request.EndGetRequestStream(asynchronousResult);
			postStream.Write(entityarg, 0, entityarg.Length);
			postStream.Flush();
			postStream.Close();
			var asyncresult = request.BeginGetResponse(new AsyncCallback(BinaryActionCompleted), request);
		}

		void BinaryActionCompleted(IAsyncResult result)
		{
			HttpWebRequest request = (HttpWebRequest)result.AsyncState;
			var action = (DocumentumAction)Enum.Parse(typeof(DocumentumAction), request.Headers["action"], true);
			switch (action)
			{
				case DocumentumAction.FetchAllByProperty:
					_syncContext.Post(FetchAllByProperty_BinaryActionCompleted, result);
					break;
				case DocumentumAction.FetchAllByBuilding:
					_syncContext.Post(FetchAllByBuilding_BinaryActionCompleted, result);
					break;
			}
		}

		HttpWebRequest GetRequest(IAsyncResult result)
		{
			return (HttpWebRequest)result.AsyncState;
		}

		HttpWebResponse GetResponse(IAsyncResult result)
		{
			var request = GetRequest(result);
			return (HttpWebResponse)request.EndGetResponse(result);
		}

		Stream GetResponseStream(IAsyncResult result)
		{
			var response = GetResponse(result);
			return response.GetResponseStream();
		}

		int ParseRecordsAffectedAsyncResult(IAsyncResult result)
		{
			var stream = GetResponseStream((IAsyncResult)result);
			return ParseRecordsAffectedStream(stream);
		}

		int ParseRecordsAffectedStream(Stream stream)
		{
			var streamreader = new StreamReader(stream);
			return Convert.ToInt32(streamreader.ReadToEnd());
		}

		private object GetDeserializedObject(object state)
		{
			HttpWebRequest request = GetRequest((IAsyncResult)state);
			HttpWebResponse response = GetResponse((IAsyncResult)state);

			var assemblyfullname = request.Headers["entityassemblyname"];
			var entityfullname = request.Headers["entityfullname"];
			var listtype = typeof(List<>).MakeGenericType(ParseType(assemblyfullname, entityfullname));
			var resp = response.GetResponseStream();
			StreamReader reader = new StreamReader(resp);

			var responseString = reader.ReadToEnd();
			var responseLength = int.Parse(responseString.Substring(0, responseString.IndexOf("@@@")).Replace("@@@", string.Empty));
			responseString = responseString.Substring(responseString.IndexOf("@@@") + 3);
			responseString = responseString.Substring(0, responseLength);

			var memstream = new MemoryStream(Convert.FromBase64String(responseString));
			var uncompresssed = Utility.DecompressHeaderless(memstream);
			var deserializedObj = _binaryformatter.Deserialize(listtype, uncompresssed);
			return deserializedObj;
		}

		protected Type ParseType(string assemblyfullname, string entityfullname)
		{
			Type retval = default(Type);
			retval = System.Reflection.Assembly.Load(assemblyfullname).GetType(entityfullname);
			return retval;
		}

		private void FetchAllByProperty_BinaryActionCompleted(object state)
		{
			object deserializedObj = GetDeserializedObject(state);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			if (OnFetchAllDocumentsForUserCompleted != null)
				OnFetchAllDocumentsForUserCompleted(new FetchResultEventArgs { Results = (IEnumerable)deserializedObj });
		}

		private void FetchAllByBuilding_BinaryActionCompleted(object state)
		{
			object deserializedObj = GetDeserializedObject(state);

			if (deserializedObj is JmrSerializationException)
			{
				RaiseException((JmrSerializationException)deserializedObj);
				return;
			}

			if (OnFetchAllDocumentsForUserByBuildingCompleted != null)
				OnFetchAllDocumentsForUserByBuildingCompleted(new FetchResultEventArgs { Results = (IEnumerable)deserializedObj });
		}

		public void RaiseException(JmrSerializationException exception)
		{
			if (OnDocumentumException != null)
				OnDocumentumException(exception);
		}
	}
}
